package com.demo.webapp.views.freemarker;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.opensymphony.xwork2.util.ValueStack;

import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 * SimpleHash的扩展模型
 * <p/>
 * 支持从多个范围进行取值操作。
 * <p/>
 * 
 * @author ryuu.kk
 */
public class ScopesHashModel extends SimpleHash implements TemplateModel {

	/**
	 * 版本序列化ID
	 */
	private static final long serialVersionUID = 5847543943209722084L;
	private HttpServletRequest request;
	private ServletContext servletContext;
	private ValueStack stack;
	private final Map<String, TemplateModel> unlistedModels = new HashMap<String, TemplateModel>();
	private volatile Object parametersCache;

	public ScopesHashModel(ObjectWrapper objectWrapper, ServletContext context,
			HttpServletRequest request, ValueStack stack) {
		super(objectWrapper);
		this.servletContext = context;
		this.request = request;
		this.stack = stack;
	}

	// This constructor is for Freemarker Sitemesh integration where the model
	// is somehow lost...
	public ScopesHashModel(ObjectWrapper objectWrapper, ServletContext context,
			HttpServletRequest request) {
		super(objectWrapper);
		this.servletContext = context;
		this.request = request;
	}

	/**
	 * 在Hash中,存储一些隐藏<tt>keys()</tt>和 <tt>values()</tt> 的方法
	 * 一般存储 Application, Session, Request, RequestParameters and JspTaglibs 对象。
	 * 
	 * @param key 参数
	 * @param model TemplateModel模型
	 */
	public void putUnlistedModel(String key, TemplateModel model) {
		unlistedModels.put(key, model);
	}

	public TemplateModel get(String key) throws TemplateModelException {
		// 查看默认范围
		TemplateModel model = super.get(key);

		if (model != null) {
			return model;
		}

		if (stack != null) {
			Object obj = findValueOnStack(key);

			if (obj != null) {
				return wrap(obj);
			}

			// 查看值栈上下文
			obj = stack.getContext().get(key);
			if (obj != null) {
				return wrap(obj);
			}
		}

		if (request != null) {
			// 查看 request scope
			Object obj = request.getAttribute(key);

			if (obj != null) {
				return wrap(obj);
			}

			// 查看 session scope
			HttpSession session = request.getSession(false);

			if (session != null) {
				obj = session.getAttribute(key);

				if (obj != null) {
					return wrap(obj);
				}
			}
		}

		if (servletContext != null) {
			// 查看 application scope
			Object obj = servletContext.getAttribute(key);

			if (obj != null) {
				return wrap(obj);
			}
		}

		// Look in unlisted models
		model = unlistedModels.get(key);
		if (model != null) {
			return wrap(model);
		}

		return null;
	}

	private Object findValueOnStack(final String key) {
		if ("parameters".equals(key)) {
			if (parametersCache != null) {
				return parametersCache;
			}
			Object parametersLocal = stack.findValue(key);
			parametersCache = parametersLocal;
			return parametersLocal;
		}
		return stack.findValue(key);
	}

	public void put(String string, boolean b) {
		super.put(string, b);
	}

	public void put(String string, Object object) {
		super.put(string, object);
	}
}
